/*
 * Copyright 2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mx.ymate.serv.impl;

import com.mx.ymate.serv.IClientHandler;
import com.mx.ymate.serv.IMxServ;
import com.mx.ymate.serv.IMxServConfig;
import com.mx.ymate.serv.IServerHandler;
import net.ymate.platform.commons.util.ClassUtils;
import net.ymate.platform.core.configuration.IConfigReader;
import net.ymate.platform.core.module.IModuleConfigurer;
import net.ymate.platform.serv.IClientCfg;
import net.ymate.platform.serv.IServerCfg;
import net.ymate.platform.serv.impl.DefaultClientCfg;
import net.ymate.platform.serv.impl.DefaultServerCfg;
import net.ymate.platform.serv.nio.INioCodec;
import net.ymate.platform.serv.nio.codec.TextLineCodec;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * DefaultMxServConfig generated By ModuleMojo on 2022/07/07 14:10
 *
 * @author YMP (https://www.ymate.net/)
 */
public final class DefaultMxServConfig implements IMxServConfig {

    private boolean enabled;


    /**
     * 获取编/解码器
     *
     * @return 获取编/解码器
     */
    private INioCodec codec;

    /**
     * 启动的端
     */
    private String client;

    /**
     * 获取服务名称
     *
     * @return 返回服务名称
     */
    private String serverName;

    /**
     * 获取主机名称或IP地址
     *
     * @return 返回主机名称或IP地址
     */
    private String serverHost;

    /**
     * 获取服务监听端口
     *
     * @return 返回服务监听端口
     */
    private int serverPort;

    /**
     * 服务端处理类
     */
    private IServerHandler serverHandler;

    /**
     * 获取字符编码
     *
     * @return 返回字符编码
     */
    private String serverCharset;

    /**
     * 获取缓冲区大小
     *
     * @return 返回缓冲区大小
     */
    private int serverBufferSize;

    /**
     * 获取执行线程数量，默认为 Runtime.getRuntime.availableProcessors
     *
     * @return 返回执行线程数量
     */
    private int serverExecutorCount;

    /**
     * 获取空闲线程等待新任务的最长时间, 默认为 0
     *
     * @return 返回空闲线程等待新任务的最长时间
     */
    private long serverKeepAliveTime;

    /**
     * 获取最大线程池大小，默认为 200
     *
     * @return 返回最大线程池大小
     */
    private int serverThreadMaxPoolSize;

    /**
     * 获取线程队列大小，默认为 1024
     *
     * @return 返回线程队列大小
     */
    private int serverThreadQueueSize;

    /**
     * 获取选择器数量
     *
     * @return 返回选择器数量
     */
    private int serverSelectorCount;

    /**
     * 获取 服务端自定义参数映射
     *
     * @return 返回服务端自定义参数映射
     */
    private Map<String, String> serverParams;

    /**
     * 获取客户端名称
     *
     * @return 返回客户端名称
     */
    private String clientName;

    /**
     * 获取远程主机名称或IP地址
     *
     * @return 返回远程主机名称或IP地址
     */
    private String clientRemoteHost;

    /**
     * 获取远程服务监听端口
     *
     * @return 返回远程服务监听端口
     */
    private int clientPort;

    /**
     * 客户端处理类
     */
    private IClientHandler clientHandler;

    /**
     * 获取字符编码
     *
     * @return 返回字符编码
     */
    private String clientCharset;

    /**
     * 获取缓冲区大小
     *
     * @return 返回缓冲区大小
     */
    private int clientBufferSize;

    /**
     * 获取执行线程数量
     *
     * @return 返回执行线程数量
     */
    private int clientExecutorCount;

    /**
     * 获取连接超时时间(秒)
     *
     * @return 返回连接超时时间
     */
    private int clientConnectionTimeout;

    /**
     * 获取断线重连检测间隔(秒)
     *
     * @return 返回断线重连检测间隔
     */
    private int clientReconnectionInterval;

    /**
     * 获取心跳包发送间隔(秒)
     *
     * @return 返回心跳包发送间隔
     */
    private int clientHeartbeatInterval;

    /**
     * 获取客户端自定义参数映射
     *
     * @return 返回客户端自定义参数映射
     */
    private Map<String, String> clientParams;

    private boolean initialized;

    public static DefaultMxServConfig defaultConfig() {
        return builder().build();
    }

    public static DefaultMxServConfig create(IModuleConfigurer moduleConfigurer) {
        return new DefaultMxServConfig(moduleConfigurer);
    }


    public static Builder builder() {
        return new Builder();
    }

    private DefaultMxServConfig() {
    }

    private DefaultMxServConfig(IModuleConfigurer moduleConfigurer) {
        IConfigReader configReader = moduleConfigurer.getConfigReader();
        enabled = configReader.getBoolean(ENABLED, true);
        client = configReader.getString(CLIENT, "all");
        String codecName = configReader.getString(CODEC);
        if (StringUtils.isNotBlank(codecName)) {
            codec = ClassUtils.impl(codecName, INioCodec.class, this.getClass());
        }
        if (codec == null) {
            codec = new TextLineCodec();
        }

        serverName = configReader.getString(SERVER_NAME, "mxServer");
        serverHost = configReader.getString(SERVER_HOST, "0.0.0.0");
        serverPort = configReader.getInt(SERVER_PORT);
        String serverHandlerName = configReader.getString(SERVER_HANDLER);
        if (StringUtils.isNotBlank(serverHandlerName)) {
            serverHandler = ClassUtils.impl(serverHandlerName, IServerHandler.class, this.getClass());
        }
        serverCharset = configReader.getString(SERVER_CHARSET);
        serverBufferSize = configReader.getInt(SERVER_BUFFER_SIZE);
        serverExecutorCount = configReader.getInt(SERVER_EXECUTOR_COUNT);
        serverKeepAliveTime = configReader.getLong(SERVER_KEEP_ALIVE_TIME);
        serverThreadMaxPoolSize = configReader.getInt(SERVER_THREAD_MAX_POOL_SIZE);
        serverThreadQueueSize = configReader.getInt(SERVER_THREAD_QUEUE_SIZE);
        serverSelectorCount = configReader.getInt(SERVER_SELECTOR_COUNT);
        String serverParamsStr = configReader.getString(SERVER_PARAMS);
        serverParams = new HashMap<>();
        if (StringUtils.isNotBlank(serverParamsStr)) {
            String[] paramsArr = serverParamsStr.split("\\|");
            for (String param : paramsArr) {
                String[] paramItem = param.split(",");
                if (paramItem.length == 2) {
                    serverParams.put(paramItem[0], paramItem[1]);
                }
            }
        }

        clientName = configReader.getString(CLIENT_NAME, "mxClient");
        clientRemoteHost = configReader.getString(CLIENT_REMOTE_HOST);
        clientPort = configReader.getInt(CLIENT_PORT);
        String clientHandlerName = configReader.getString(CLIENT_HANDLER);
        if (StringUtils.isNotBlank(clientHandlerName)) {
            clientHandler = ClassUtils.impl(clientHandlerName, IClientHandler.class, this.getClass());
        }
        clientCharset = configReader.getString(CLIENT_CHARSET);
        clientBufferSize = configReader.getInt(CLIENT_BUFFER_SIZE);
        clientExecutorCount = configReader.getInt(CLIENT_EXECUTOR_COUNT);
        clientConnectionTimeout = configReader.getInt(CLIENT_CONNECTION_TIMEOUT);
        clientReconnectionInterval = configReader.getInt(CLIENT_RECONNECTION_INTERVAL);
        clientHeartbeatInterval = configReader.getInt(CLIENT_HEARTBEAT_INTERVAL);

        String clientParamsStr = configReader.getString(CLIENT_PARAMS);
        clientParams = new HashMap<>();
        if (StringUtils.isNotBlank(clientParamsStr)) {
            String[] paramsArr = clientParamsStr.split("\\|");
            for (String param : paramsArr) {
                String[] paramItem = param.split(",");
                if (paramItem.length == 2) {
                    clientParams.put(paramItem[0], paramItem[1]);
                }
            }
        }
    }

    @Override
    public void initialize(IMxServ owner) throws Exception {
        if (!initialized) {
            if (enabled) {
                // TODO What to do?
            }
            initialized = true;
        }
    }

    @Override
    public boolean isInitialized() {
        return initialized;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    @Override
    public INioCodec getCodec() {
        return this.codec;
    }

    @Override
    public String getClient() {
        return this.client;
    }

    @Override
    public String getServerName() {
        return this.serverName;
    }

    @Override
    public String getServerHost() {
        return this.serverHost;
    }

    @Override
    public int getServerPort() {
        return this.serverPort;
    }

    @Override
    public IServerHandler getServerHandler() {
        return this.serverHandler;
    }

    @Override
    public String getServerCharset() {
        return this.serverCharset;
    }

    @Override
    public int getServerBufferSize() {
        return this.serverBufferSize;
    }

    @Override
    public int getServerExecutorCount() {
        return this.serverExecutorCount;
    }

    @Override
    public long getServerKeepAliveTime() {
        return this.serverKeepAliveTime;
    }

    @Override
    public int getServerThreadMaxPoolSize() {
        return this.serverThreadMaxPoolSize;
    }

    @Override
    public int getServerThreadQueueSize() {
        return this.serverThreadQueueSize;
    }

    @Override
    public int getServerSelectorCount() {
        return this.serverSelectorCount;
    }

    @Override
    public Map<String, String> getServerParams() {
        return this.serverParams;
    }

    @Override
    public String getClientName() {
        return this.clientName;
    }

    @Override
    public IClientHandler getClientHandler() {
        return this.clientHandler;
    }

    @Override
    public String getClientRemoteHost() {
        return this.clientRemoteHost;
    }

    @Override
    public int getClientPort() {
        return this.clientPort;
    }

    @Override
    public String getClientCharset() {
        return this.clientCharset;
    }

    @Override
    public int getClientBufferSize() {
        return this.clientBufferSize;
    }

    @Override
    public int getClientExecutorCount() {
        return this.clientExecutorCount;
    }

    @Override
    public int getClientConnectionTimeout() {
        return this.clientConnectionTimeout;
    }

    @Override
    public int getClientReconnectionInterval() {
        return this.clientReconnectionInterval;
    }

    @Override
    public int getClientHeartbeatInterval() {
        return this.clientHeartbeatInterval;
    }

    @Override
    public Map<String, String> getClientParams() {
        return this.clientParams;
    }

    @Override
    public IServerCfg toServerCfg() {
        return DefaultServerCfg.builder()
                .serverName(this.serverName)
                .serverHost(this.serverHost)
                .port(this.serverPort)
                .bufferSize(this.serverBufferSize)
                .charset(this.serverCharset)
                .executorCount(this.serverExecutorCount)
                .keepAliveTime(this.serverKeepAliveTime)
                .params(this.serverParams)
                .selectorCount(this.serverSelectorCount)
                .threadMaxPoolSize(this.serverThreadMaxPoolSize)
                .threadQueueSize(this.serverThreadQueueSize)
                .build();
    }

    @Override
    public IClientCfg toClientCfg() {
        return DefaultClientCfg.builder()
                .clientName(this.clientName)
                .remoteHost(this.clientRemoteHost)
                .port(this.clientPort)
                .bufferSize(this.clientBufferSize)
                .charset(this.clientCharset)
                .params(this.clientParams)
                .connectionTimeout(this.clientConnectionTimeout)
                .executorCount(this.clientExecutorCount)
                .heartbeatInterval(this.clientHeartbeatInterval)
                .reconnectionInterval(this.clientReconnectionInterval)
                .build();
    }

    public void setEnabled(boolean enabled) {
        if (!initialized) {
            this.enabled = enabled;
        }
    }

    public void setCodec(INioCodec codec) {
        if (!initialized) {
            this.codec = codec;
        }
    }

    public void setClient(String client) {
        if (!initialized) {
            this.client = client;
        }
    }

    public void setServerName(String serverName) {
        if (!initialized) {
            this.serverName = serverName;
        }
    }

    public void setServerHost(String serverHost) {
        if (!initialized) {
            this.serverHost = serverHost;
        }
    }

    public void setServerPort(int serverPort) {
        if (!initialized) {
            this.serverPort = serverPort;
        }
    }

    public void setServerHandler(IServerHandler serverHandler) {
        if (!initialized) {
            this.serverHandler = serverHandler;
        }
    }

    public void setServerCharset(String serverCharset) {
        if (!initialized) {
            this.serverCharset = serverCharset;
        }
    }

    public void setServerBufferSize(int serverBufferSize) {
        if (!initialized) {
            this.serverBufferSize = serverBufferSize;
        }
    }

    public void setServerExecutorCount(int serverExecutorCount) {
        if (!initialized) {
            this.serverExecutorCount = serverExecutorCount;
        }
    }

    public void setServerKeepAliveTime(long serverKeepAliveTime) {
        if (!initialized) {
            this.serverKeepAliveTime = serverKeepAliveTime;
        }
    }

    public void setServerThreadMaxPoolSize(int serverThreadMaxPoolSize) {
        if (!initialized) {
            this.serverThreadMaxPoolSize = serverThreadMaxPoolSize;
        }
    }

    public void setServerThreadQueueSize(int serverThreadQueueSize) {
        if (!initialized) {
            this.serverThreadQueueSize = serverThreadQueueSize;
        }
    }

    public void setServerSelectorCount(int serverSelectorCount) {
        if (!initialized) {
            this.serverSelectorCount = serverSelectorCount;
        }
    }

    public void setServerParams(Map<String, String> serverParams) {
        if (!initialized) {
            this.serverParams = serverParams;
        }
    }

    public void setClientName(String clientName) {
        if (!initialized) {
            this.clientName = clientName;
        }
    }

    public void setClientRemoteHost(String clientRemoteHost) {
        if (!initialized) {
            this.clientRemoteHost = clientRemoteHost;
        }
    }

    public void setClientPort(int clientPort) {
        if (!initialized) {
            this.clientPort = clientPort;
        }
    }

    public void setClientHandler(IClientHandler clientHandler) {
        if (!initialized) {
            this.clientHandler = clientHandler;
        }
    }

    public void setClientCharset(String clientCharset) {
        if (!initialized) {
            this.clientCharset = clientCharset;
        }
    }

    public void setClientBufferSize(int clientBufferSize) {
        if (!initialized) {
            this.clientBufferSize = clientBufferSize;
        }
    }

    public void setClientExecutorCount(int clientExecutorCount) {
        if (!initialized) {
            this.clientExecutorCount = clientExecutorCount;
        }
    }

    public void setClientConnectionTimeout(int clientConnectionTimeout) {
        if (!initialized) {
            this.clientConnectionTimeout = clientConnectionTimeout;
        }
    }

    public void setClientReconnectionInterval(int clientReconnectionInterval) {
        if (!initialized) {
            this.clientReconnectionInterval = clientReconnectionInterval;
        }
    }

    public void setClientHeartbeatInterval(int clientHeartbeatInterval) {
        if (!initialized) {
            this.clientHeartbeatInterval = clientHeartbeatInterval;
        }
    }

    public void setClientParams(Map<String, String> clientParams) {
        if (!initialized) {
            this.clientParams = clientParams;
        }
    }

    public static final class Builder {

        private final DefaultMxServConfig config = new DefaultMxServConfig();

        private Builder() {
        }

        public Builder enabled(boolean enabled) {
            config.setEnabled(enabled);
            return this;
        }

        public DefaultMxServConfig build() {
            return config;
        }
    }
}